package lol.clann.bukkit.pluginboot.util;

import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.annotation.svc.PostInstance;
import lol.clann.clannboot.annotation.svc.Service;
import lol.clann.clannboot.context.ModuleContext;
import lol.clann.clannboot.util.BaseUtils;
import lol.clann.clannboot.util.clazz.*;
import com.google.common.collect.Lists;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.nutz.lang.Lang;
import org.slf4j.Logger;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author pyz
 * @date 2018/7/22 下午6:53
 */
@Service(priority = Integer.MAX_VALUE)
public class ItemStackUtils {

    public static final WrapClass classNMSPlayerInteractManager = ClassUtils.forName("{nms}.PlayerInteractManager");
    public static final WrapClass classNMSItemStack = ClassUtils.forName("{nms}.ItemStack");
    public static final WrapClass classNMSItem = ClassUtils.forName("{nms}.Item");
    public static final WrapClass classMovingObjectPosition = ClassUtils.forName("{nms}.MovingObjectPosition");
    public static final WrapClass classCraftMagicNumbers = ClassUtils.forName("{cb}.util.CraftMagicNumbers");
    public static final WrapClass classCraftItemStack = ClassUtils.forName("{cb}.inventory.CraftItemStack");
    private static Map<String, String> attributes = new HashMap();
    private static File itemDir;
    private static File invDir;

    static {
        attributes.put("最大生命值", "generic.maxHealth");
        attributes.put("生物跟随距离", "generic.followRange");
        attributes.put("击退抗性", "generic.knockbackResistance");
        attributes.put("速度", "generic.movementSpeed");
        attributes.put("攻击伤害", "generic.attackDamage");
        attributes.put("generic.maxHealth", "最大生命值");
        attributes.put("generic.followRange", "生物跟随距离");
        attributes.put("generic.knockbackResistance", "击退抗性");
        attributes.put("generic.movementSpeed", "速度");
        attributes.put("generic.attackDamage", "攻击伤害");
    }

    @Inject
    private Validator validator;
    @Inject
    private Logger logger;
    private WrapMethod<String> getDisplayName;
    private WrapMethod getEnchantmentTagList;
    private WrapMethod getItem;
    private WrapMethod<Integer> getMaxDamage;
    private WrapMethod<Integer> getMaxDurability;
    private WrapMethod<Integer> getMaxStackSize;
    private WrapMethod<Integer> getRepairCost;
    private WrapMethod getTagCompound;
    private WrapMethod<String> getUnlocalizedName;
    private WrapMethod<Boolean> hasDisplayName;
    private WrapMethod<Boolean> isItemEnchantable;
    private WrapMethod<Boolean> isItemEnchanted;
    private WrapMethod<Boolean> isStackable;
    private WrapMethod loadItemStackFromNBT;
    private Constructor<?> createItemStackFromNBT;
    private WrapMethod readFromNBT;
    private WrapMethod setRepairCost;
    private WrapMethod setTagCompound;
    private WrapMethod writeToNBT;
    private WrapMethod<ItemStack> asCraftCopy;
    private WrapField handle;
    private WrapMethod<Material> getItemStackMaterial;
    private WrapMethod<ItemStack> asBukkitCopy;
    private WrapMethod getMovingObjectPosition;
    private WrapMethod getItemFromMaterial;

    @Inject
    private NBTUtils nbtUtils;
    @Inject
    private NBTListUtils nbtListUtils;
    @Inject
    private NBTCompoundUtils nbtCompoundUtils;
    @Inject
    private WorldUtils worldUtils;
    @Inject
    private PlayerUtils playerUtils;
    @Inject
    private ChatSerializerUtils chatSerializerUtils;
    @Inject
    private EntityUtils entityUtils;

    private Object nmsItemStone;

    private static String getEnchKey(ItemStack is) {
        if (is.getType().equals(Material.ENCHANTED_BOOK)) {
            return "StoredEnchantments";
        } else {
            return "ench";
        }
    }

    @PostInstance
    private void init() {
        itemDir = new File(BaseUtils.resolve(ModuleContext.rootModuleId, "{.}/data/common/itemSaveData/item")[0]);
        if (!itemDir.exists()) {
            itemDir.mkdirs();
        }
        invDir = new File(BaseUtils.resolve(ModuleContext.rootModuleId, "{.}/data/common/itemSaveData/inv")[0]);
        if (!invDir.exists()) {
            invDir.mkdirs();
        }

        getDisplayName = classNMSItemStack.getMethod(MethodFilter.build().name("getDisplayName", "getName"));
        getEnchantmentTagList = classNMSItemStack.getMethod(MethodFilter.build().name("getEnchantmentTagList", "getEnchantments"));
        getItem = classNMSItemStack.getMethod(MethodFilter.build().name("getItem"));
        getMaxStackSize = classNMSItemStack.getMethod(MethodFilter.build().name("getMaxStackSize"));
        getRepairCost = classNMSItemStack.getMethod(MethodFilter.build().name("getRepairCost"));
        getTagCompound = classNMSItemStack.getMethod(MethodFilter.build().name("getTagCompound", "getTag"));
        hasDisplayName = classNMSItemStack.getMethod(MethodFilter.build().name("hasDisplayName", "hasName"));
        isItemEnchantable = classNMSItemStack.getMethod(MethodFilter.build().name("isItemEnchantable", "canEnchant"));
        isItemEnchanted = classNMSItemStack.getMethod(MethodFilter.build().name("isItemEnchanted", "hasEnchantments"));
        isStackable = classNMSItemStack.getMethod(MethodFilter.build().name("isStackable"));
        try {
            loadItemStackFromNBT = classNMSItemStack.getMethod(MethodFilter.build().static_(true).parameterType(NBTUtils.classNBTTagCompound).returnType(classNMSItemStack));
        } catch (Throwable e) {
            createItemStackFromNBT = classNMSItemStack.getConstructor(ParameterCondition.of(NBTUtils.classNBTTagCompound.getHandle()));
        }
        readFromNBT = classNMSItemStack.getMethod(MethodFilter.build().name("readFromNBT", "load"));
        setRepairCost = classNMSItemStack.getMethod(MethodFilter.build().name("setRepairCost"));
        setTagCompound = classNMSItemStack.getMethod(MethodFilter.build().name("setTagCompound", "setTag"));
        writeToNBT = classNMSItemStack.getMethod(MethodFilter.build().returnType(NBTUtils.classNBTTagCompound).parameterType(NBTUtils.classNBTTagCompound));

        getMaxDurability = classNMSItem.getMethod(MethodFilter.build().name("getMaxDurability"));
        getUnlocalizedName = classNMSItem.getMethod(MethodFilter.build().name("getUnlocalizedName", "getName", "func_77658_a").emptyParameter());

        //craftBukkit
        handle = classCraftItemStack.getField("handle");
        asBukkitCopy = classCraftItemStack.getMethod("asBukkitCopy");
        asCraftCopy = classCraftItemStack.getMethod("asCraftCopy");

        getItemStackMaterial = classCraftMagicNumbers.getMethod(MethodFilter.build().name("getMaterial").returnType(Material.class).parameterType(classNMSItem));
        getItemFromMaterial = classCraftMagicNumbers.getMethod(MethodFilter.build().name("getItem").returnType(classNMSItem).parameterType(Material.class));

        getMovingObjectPosition = classNMSItem.getMethod(MethodFilter.build().returnType(classMovingObjectPosition).parameterType(WorldUtils.classNMSWorld.getHandle(), EntityUtils.classNMSEntityHuman.getHandle(), boolean.class));

        nmsItemStone = getItem(Material.STONE);
    }

    /**
     * @param material
     * @return nmsItem
     */
    public Object getItem(Material material) {
        return getItemFromMaterial.invoke(null, material);
    }

    public Object getMovingObjectPosition(LivingEntity livingEntity, boolean containsFluid) {
        return getMovingObjectPosition.invoke(nmsItemStone, worldUtils.toNMS(livingEntity.getWorld()), entityUtils.toNMS(livingEntity), containsFluid);
    }

    public ItemStack asCraftCopy(ItemStack itemStack) {
        return asCraftCopy.invoke(null, itemStack);
    }

    public Material getMaterial(Object itemStack) {
        if (itemStack instanceof ItemStack) {
            return ((ItemStack) itemStack).getType();
        } else {
            return getItemStackMaterial.invoke(null, itemStack);
        }
    }

    public Object getItem(Object itemStack) {
        if (itemStack == null) {
            return null;
        }
        if (itemStack instanceof ItemStack) {
            itemStack = toNMS((ItemStack) itemStack);
        }
        return getItem.invoke(itemStack);
    }

    public Integer getMaxDamage(ItemStack itemStack) {
        return getMaxDamage.invoke(toNMS(itemStack));
    }

    public Integer getMaxItemUseDuration(ItemStack itemStack) {
        return getMaxDurability.invoke(getItem(itemStack));
    }

    public Integer getMaxStackSize(ItemStack itemStack) {
        return getMaxStackSize.invoke(toNMS(itemStack));
    }

    public Integer getRepairCost(ItemStack itemStack) {
        return getRepairCost.invoke(toNMS(itemStack));
    }

    public String getUnlocalizedName(Object handle) {
        if (handle instanceof ItemStack) {
            handle = toNMS((ItemStack) handle);
        }
        handle = getItem(handle);
        return getUnlocalizedName.invoke(handle);
    }

    public Boolean hasDisplayName(Object handle) {
        return hasDisplayName.invoke(toNMS((ItemStack) handle));
    }

    public Boolean isItemEnchantable(Object handle) {
        return isItemEnchantable.invoke(handle);
    }

    public Boolean isItemEnchanted(Object handle) {
        return isItemEnchanted.invoke(handle);
    }

    public Boolean isStackable(Object handle) {
        return isStackable.invoke(handle);
    }

    public void readFromNBT(ItemStack handle, Object nbtTag) {
        readFromNBT.invoke(toNMS(handle), nbtTag);
    }

    public void setRepairCost(Object handle, int arg0) {
        setRepairCost.invoke(handle, arg0);
    }

    public ItemStack setStackDisplayName(ItemStack itemStack, String name) {
        ItemMeta meta = itemStack.getItemMeta();
        meta.setDisplayName(name);
        itemStack.setItemMeta(meta);
        return itemStack;
    }

    public void setTagCompound(ItemStack handle, Object nbtTagCompound) {
        setTagCompound.invoke(toNMS(handle), nbtTagCompound);
    }

    /**
     * @param handle
     * @param nbtTag
     * @return NBTTagCompound
     */
    public Object writeToNBT(Object handle, Object nbtTag) {
        if (handle instanceof ItemStack) {
            handle = toNMS((ItemStack) handle);
        }
        return writeToNBT.invoke(handle, nbtTag);
    }

    public String getNBTFormatJson(ItemStack item) {
        if (isEmpty(item)) {
            return "{}";
        }
        StringBuilder itemJson = new StringBuilder("{id:");
        itemJson.append(item.getTypeId()).append('s');
        itemJson.append(",Damage:").append(item.getDurability());
        Object tag = getTagCompound(item, false);
        if (tag != null) {
            itemJson.append(",tag:").append(nbtUtils.getNBTFormatJson(tag));
        }
        itemJson.append('}');
        return itemJson.toString();
    }

    public void saveItemStack(String key, ItemStack itemStack, boolean override) {
        File f = new File(itemDir, key);
        if (!override && f.exists()) {
            throw new org.bukkit.command.CommandException("该物品已存在:" + key);
        }
        Object data = toCompound(itemStack);
        nbtCompoundUtils.write(data, f);
    }

    public ItemStack getItemStack(String key) {
        File f = new File(itemDir, key);
        validator.exist(f, "该物品不存在:" + key);
        Object tag = nbtCompoundUtils.read(f);
        return fromCompound(tag);
    }

    public void deleteItemStack(String key) {
        File f = new File(itemDir, key);
        if (f.exists()) {
            f.delete();
        }
    }

    public List<String> listItemStackKeys() {
        return Arrays.stream(itemDir.listFiles())
                .map(f -> f.getName())
                .collect(Collectors.toList());
    }

    public void saveItemStacks(String key, List<ItemStack> itemStack, boolean override) {
        File f = new File(invDir, key);
        if (!override && f.exists()) {
            throw new org.bukkit.command.CommandException("背包已存在:" + key);
        }
        Object data = nbtCompoundUtils.newTagCompound();
        Object list = nbtListUtils.newList();
        for (ItemStack stack : itemStack) {
            if (isEmpty(stack)) {
                continue;
            }
            nbtListUtils.appendTag(list, toCompound(stack));
        }
        nbtCompoundUtils.setTag(data, "items", list);
        nbtCompoundUtils.write(data, f);
    }

    public List<ItemStack> getItemStacks(String key) {
        File file = new File(invDir, key);
        validator.exist(file, "背包 " + key + " 不存在");
        Object tag = nbtCompoundUtils.read(new File(invDir, key));
        Object list = nbtCompoundUtils.getTagList(tag, "items");
        int size = nbtListUtils.getSize(list);
        List<ItemStack> is = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            is.add(fromCompound(nbtListUtils.getCompoundTagAt(list, i)));
        }
        return is;
    }

    public void deleteItemStacks(String key) {
        File f = new File(invDir, key);
        if (f.exists()) {
            f.deleteOnExit();
        }
    }

    public List<String> listItemStacksKeys() {
        return Arrays.stream(invDir.listFiles())
                .map(f -> f.getName())
                .collect(Collectors.toList());
    }


    /**
     * 设置物品不掉耐久
     *
     * @return
     */
    public ItemStack setUnbreakable(ItemStack is, boolean unbreakable) {
        Object tag = getTagCompound(is, true);
        if (unbreakable) {
            nbtCompoundUtils.setByte(tag, "Unbreakable", (byte) 1);
        } else {
            nbtCompoundUtils.removeTag(tag, "Unbreakable");
        }
        return is;
    }

    /**
     * 判断两个物品是否相似
     */
    public boolean isSimilar(ItemStack i1, ItemStack i2) {
        return
                i1.getType().equals(i2.getType())
                        && i1.getDurability() == i2.getDurability()
                        && Objects.equals(getTagCompound(i1, false), getTagCompound(i2, false));
    }

    /**
     * 返回非空Lore
     *
     * @param is
     * @return
     */
    public List<String> getLore(ItemStack is) {
        return Optional.ofNullable(getTagCompound(is, false))
                .map(tag -> nbtCompoundUtils.getCompoundTag(tag, "display"))
                .map(display -> nbtCompoundUtils.getTagList(display, "Lore"))
                .map(lore -> nbtListUtils.getStringValue(lore))
                .orElse(new ArrayList<>());
    }

    /**
     * 设置Lore
     *
     * @param is
     * @return
     */
    public ItemStack setLore(ItemStack is, List<String> lore) {
        Object tag = getTagCompound(is, true);
        Object display = nbtCompoundUtils.getCompoundTag(tag, "display");
        if (display == null) {
            display = nbtCompoundUtils.newTagCompound();

            nbtCompoundUtils.setTag(tag, "display", display);
        }
        Object Lore = nbtListUtils.newList();
        nbtCompoundUtils.setTag(display, "Lore", Lore);
        if (lore == null || lore.isEmpty()) {
            return is;
        }
        for (String s : lore) {
            nbtListUtils.appendTag(Lore, nbtUtils.newNBTString(s == null ? "" : s));
        }
        return is;
    }

    /**
     * 设置Lore
     *
     * @param is
     * @return
     */
    public ItemStack setLore(ItemStack is, int index, String add) {
        List<String> lore = getLore(is);
        lore.set(index, add);
        setLore(is, lore);
        return is;
    }

    /**
     * 为物品添加Lore，支持颜色字符
     *
     * @param is
     * @param s
     * @return
     */
    public ItemStack addLore(ItemStack is, String s) {
        List<String> lore = getLore(is);
        lore.add("§f" + s.replace('&', '§'));
        return setLore(is, lore);
    }

    /**
     * 移除物品所有Lore
     *
     * @return
     */
    public ItemStack removeLore(ItemStack is) {
        return setLore(is, new ArrayList<>());
    }

    /**
     * 移除物品指定Lore
     *
     * @param i
     * @return
     */
    public ItemStack removeLore(ItemStack is, int i) {
        List<String> lore = getLore(is);
        lore.remove(i);
        return setLore(is, lore);
    }

    public boolean isEmpty(ItemStack itemStack) {
        return itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() < 0;
    }

    /**
     * @param itemStack
     * @return nmsItem
     */
    public Object toItem(ItemStack itemStack) {
        return getItem.invoke(toNMS(itemStack));
    }

    public String getUnlocalizedName(ItemStack itemStack) {
        return getUnlocalizedName.invoke(toNMS(itemStack));
    }

    public String getDisplayName(ItemStack itemStack) {
        Object nms = toNMS(itemStack);
        if (nms != null) {
            Object name = getDisplayName.invoke(nms);
            if (name instanceof String) {
                return (String) name;
            }
            return chatSerializerUtils.serialize(name);
        } else {
            ItemMeta meta = itemStack.getItemMeta();
            return meta.getDisplayName();
        }
    }

    public boolean isItemEnchanted(ItemStack itemStack) {
        return isItemEnchanted.invoke(toNMS(itemStack));
    }

    public void setRepairCost(ItemStack itemStack, int repairCost) {
        setRepairCost.invoke(toNMS(itemStack), repairCost);
    }

//    public NBTTagCompound getTagCompound(ItemStack itemStack) {
//        NBTTagCompound tag = getTagCompound.invoke(toNMS(itemStack));
//        return tag;
//    }

    /**
     * @param itemStack
     * @param create
     * @return NBTTagCompound
     */
    public Object getTagCompound(ItemStack itemStack, boolean create) {
        Object tag = getTagCompound.invoke(toNMS(itemStack));
        if (tag == null && create) {
            tag = nbtCompoundUtils.newTagCompound();
            setTagCompound(itemStack, tag);
        }
        return tag;
    }

    public Object createTag(ItemStack itemStack) {
        Object tag = nbtCompoundUtils.newTagCompound();
        setTagCompound(itemStack, tag);
        return tag;
    }

    /**
     * 将指定份数的物品碟堆起来
     *
     * @param itemStack
     * @param mutiple
     * @return
     */
    public List<ItemStack> merge(ItemStack itemStack, int mutiple) {
        return organise(itemStack, itemStack.getAmount() * mutiple);
    }

    /**
     * 将指定数量的物品进行碟堆,返回碟堆后的物品
     * 碟堆后的物品,除了最后一份,其余都是一堆的最大数量
     *
     * @param itemStack
     * @param quantity
     * @return
     */
    public List<ItemStack> organise(ItemStack itemStack, int quantity) {
        int big = quantity / itemStack.getMaxStackSize();
        int small = quantity % itemStack.getMaxStackSize();

        itemStack = itemStack.clone();

        List<ItemStack> items = new ArrayList<>(big + (small > 0 ? 1 : 0));

        itemStack.setAmount(itemStack.getMaxStackSize());

        for (int i = 0; i < big; i++) {
            items.add(itemStack.clone());
        }
        if (small > 0) {
            itemStack.setAmount(small);
            items.add(itemStack.clone());
        }
        return items;
    }

    public ItemStack copy(ItemStack itemStack) {
        return itemStack.clone();
    }

    /**
     * @param itemStack
     * @return NBTTagCompound
     */
    public Object toCompound(Object itemStack) {
        Object tag = nbtUtils.newNBTCompound();
        if (itemStack instanceof ItemStack) {
            writeToNBT.invoke(toNMS(itemStack), tag);
            nbtCompoundUtils.removeTag(tag, "id");
            nbtCompoundUtils.setString(tag, "id", ((ItemStack) itemStack).getType().name());
        } else {
            writeToNBT.invoke(itemStack, tag);
            nbtCompoundUtils.removeTag(tag, "id");
            Material m = getMaterial(itemStack);
            nbtCompoundUtils.setString(tag, "id", m.name());
        }
        return tag;
    }

    public ItemStack fromCompound(Object nbtTag) {
        String id = nbtCompoundUtils.getString(nbtTag, "id");
        Material m = Material.getMaterial(id);
        if (m == null) {
            throw new RuntimeException("无效的物品:" + id);
        }
        nbtCompoundUtils.removeTag(nbtTag, "id");
        nbtCompoundUtils.setShort(nbtTag, "id", (short) m.getId());
        Object nms;
        if (loadItemStackFromNBT == null) {
            nms = loadItemStackFromNBT.invoke(null, nbtTag);
        } else {
            try {
                nms = createItemStackFromNBT.newInstance(nbtTag);
            } catch (Throwable e) {
                throw Lang.wrapThrow(e);
            }
        }
        return toBukkit(nms);
    }

    public void fill(Object itemStack, Object nbtTag) {
        Object nms = itemStack instanceof ItemStack ? toNMS((ItemStack) itemStack) : itemStack;
        readFromNBT.invoke(nms, nbtTag);
    }

    public Object toNMS(Object itemStack) {
        if (itemStack == null) {
            return null;
        }
        if (itemStack instanceof ItemStack) {
            return handle.get(itemStack);
        }
        return itemStack;
    }


    public ItemStack toBukkit(Object nms) {
        return asBukkitCopy.invoke(null, nms);
    }

    public ItemStack removeAddtionalData(ItemStack is) {
        NBTEditor.fromHandle(is)
                .remove("display")
                .remove("ench")
                .remove("RepairCost");
        return is;
    }

    /**
     * 为装备添加属性 1.生命值generic.maxHealth 2.跟随距离generic.followRange
     * 3.击退抗性generic.knockbackResistance 4.移动速度generic.movementSpeed
     * 5.攻击伤害generic.attackDamage
     *
     * @param item
     * @param type
     * @param amount
     * @return
     */
    public ItemStack addAttributeModifiers(ItemStack item, String type, double amount) {
        Object tag = getTagCompound(item, true);
        Object AttributeModifiers = nbtCompoundUtils.getTag(tag, "AttributeModifiers");
        if (AttributeModifiers == null) {
            AttributeModifiers = nbtListUtils.newList();
            nbtCompoundUtils.setTag(tag, "AttributeModifiers", AttributeModifiers);
        }
        int size = nbtListUtils.getSize(AttributeModifiers);
        for (int i = 0; i < size; i++) {
            Object base = nbtListUtils.getCompoundTagAt(AttributeModifiers, i);
            if (nbtUtils.isTagCompound(base)) {
                if (type.equals(nbtCompoundUtils.getString(base, "AttributeName"))) {
                    nbtCompoundUtils.setDouble(base, "Amount", amount);
                    return item;
                }
            }
        }
        nbtListUtils.appendTag(AttributeModifiers, createAttributeModifiers(type, amount));
        return item;
    }

    private Object createAttributeModifiers(String name, double num) {
        Object tag = nbtCompoundUtils.newTagCompound();
        nbtCompoundUtils.setLong(tag, "UUIDMost", 2872L);
        nbtCompoundUtils.setLong(tag, "UUIDLeast", 894654L);
        nbtCompoundUtils.setDouble(tag, "Amount", num);
        nbtCompoundUtils.setString(tag, "AttributeName", name);
        nbtCompoundUtils.setInteger(tag, "Operation", 0);
        nbtCompoundUtils.setString(tag, "Name", name);
        return tag;
    }

    private void setEnchantmentTagList(ItemStack is, Object nbtTagList) {
        String key = getEnchKey(is);
        Object tag = getTagCompound(is, true);
        nbtCompoundUtils.setTag(tag, key, nbtTagList);
    }

    /**
     * @param is
     * @return NBTTagList
     */
    private Object getEnchantmentTagList(ItemStack is) {
        String key = getEnchKey(is);
        Object tag = getTagCompound(is, false);
        if (tag == null) {
            return null;
        }
        return nbtCompoundUtils.getTagList(tag, key);
    }

    public ItemStack addEnchantment(ItemStack is, short id, short level) {
        Object list = getEnchantmentTagList(is);
        if (list == null) {
            list = nbtListUtils.newList();
        }
        Object ench = nbtUtils.newNBTCompound();
        nbtCompoundUtils.setShort(ench, "id", id);
        nbtCompoundUtils.setShort(ench, "lvl", level);
        nbtListUtils.appendTag(list, ench);
        setEnchantmentTagList(is, list);
        return is;
    }

    public ItemStack setEnchantment(ItemStack is, int index, short id, short level) {
        Object enchantments = getEnchantmentTagList(is);
        Object ench = nbtUtils.newNBTCompound();
        nbtCompoundUtils.setShort(ench, "id", id);
        nbtCompoundUtils.setShort(ench, "lvl", level);
        nbtListUtils.setTag(enchantments, index, ench);
        return is;
    }

    public Enchantment getEnchantmentById(short id) {
        Enchantment en = Enchantment.getById(id);
        if (en == null) {
            throw new RuntimeException("未知附魔ID:" + id);
        }
        return en;
    }

    /**
     * 为附魔书添加附魔
     *
     * @param level
     * @return
     * @throws Exception
     */
    public ItemStack addEnchantment(ItemStack is, Enchantment en, short level) {
        return addEnchantment(is, (short) en.getId(), level);
    }

    /**
     * 设置附魔书附魔
     *
     * @param level
     * @return
     * @throws Exception
     */
    public ItemStack setEnchantment(ItemStack is, int index, Enchantment en, short level) {
        return setEnchantment(is, index, (short) en.getId(), level);
    }

    /**
     * 设置附魔书附魔
     */
    public ItemStack removeEnchantment(ItemStack is, int index) {
        Object enchantments = getEnchantmentTagList(is);
        nbtListUtils.removeTag(enchantments, index);
        return is;
    }

    /**
     * 移除物品所有附魔
     *
     * @param is
     * @return
     */
    public ItemStack removeEnchantments(ItemStack is) {
        Object enchantments = getEnchantmentTagList(is);
        nbtListUtils.getCompoundValue(enchantments).clear();
        return is;
    }

    /**
     * 移除物品所有属性
     *
     * @param is
     * @return
     */
    public ItemStack removeAttributes(ItemStack is) {
        Object tag = getTagCompound(is, false);
        if (tag != null) {
            nbtCompoundUtils.removeTag(tag, "AttributeModifiers");
        }
        return is;
    }

    /**
     * 移除物品指定属性
     *
     * @param is
     * @return
     */
    public ItemStack removeAttribute(ItemStack is, String name) {
        Object tag = getTagCompound(is, false);
        if (tag == null) {
            return is;
        }
        Object AttributeModifiers = nbtCompoundUtils.getTag(tag, "AttributeModifiers");
        int size = nbtListUtils.getSize(AttributeModifiers);
        if (AttributeModifiers == null || size == 0) {
            return is;
        }
        for (int i = 0; i < size; i++) {
            Object base = nbtListUtils.getCompoundTagAt(AttributeModifiers, i);
            if (nbtUtils.isTagCompound(base)) {
                if (name.equals(nbtCompoundUtils.getString(base, "AttributeName"))) {
                    nbtListUtils.removeTag(AttributeModifiers, i);
                    return is;
                }
            }
        }
        return is;
    }

    public ItemStack insertLore(ItemStack itemStack, String... lores) {
        List<String> newlore = Lists.newArrayList();
        List<String> existLore = getLore(itemStack);
        newlore.addAll(Arrays.asList(lores));
        newlore.addAll(existLore);
        setLore(itemStack, newlore);
        return itemStack;
    }

    public ItemStack appendLore(ItemStack itemStack, String... lores) {
        List<String> existLore = getLore(itemStack);
        existLore.addAll(Arrays.asList(lores));
        setLore(itemStack, existLore);
        return itemStack;
    }

    public ItemStack addAttribute(ItemStack itemStack, String name, double value) {
        String key = attributes.get(name);
        if (key == null) {
            return itemStack;
        }
        Object tag = getTagCompound(itemStack, false);
        if (tag == null) {
            tag = nbtCompoundUtils.newTagCompound();
            setTagCompound(itemStack, tag);
        }
        Object AttributeModifiers = nbtCompoundUtils.getTagList(tag, "AttributeModifiers");
        if (AttributeModifiers == null) {
            AttributeModifiers = nbtListUtils.newList();
            nbtCompoundUtils.setTag(tag, "AttributeModifiers", AttributeModifiers);
        }
        Object attribute = nbtCompoundUtils.newTagCompound();
        nbtCompoundUtils.setLong(attribute, "UUIDMost", 2872L);
        nbtCompoundUtils.setLong(attribute, "UUIDLeast", 894654L);
        nbtCompoundUtils.setDouble(attribute, "Amount", value);
        nbtCompoundUtils.setString(attribute, "AttributeName", key);
        nbtCompoundUtils.setInteger(attribute, "Operation", 0);
        nbtCompoundUtils.setString(attribute, "Name", key);
        int size = nbtListUtils.getSize(AttributeModifiers);
        for (int i = 0; i < size; i++) {
            Object att = nbtListUtils.getCompoundTagAt(AttributeModifiers, i);
            if (key.equals(nbtCompoundUtils.getString(att, "AttributeName"))) {
                List tagList = (List) nbtUtils.getValue(AttributeModifiers);
                tagList.set(i, attribute);
                return itemStack;
            }
        }
        nbtListUtils.appendTag(AttributeModifiers, attribute);
        return itemStack;
    }

    public String getTypeId(ItemStack itemStack) {
        return itemStack.getType().name();
    }

    public String getStackId(ItemStack itemStack) {
        return itemStack.getType().name() + ":" + itemStack.getDurability();
    }

}
